Spring boot로 REST API 구현(JPA)

✒️ 2025-06-24 11:05 내용 수정

스프링부트3 자바 백엔드 개발입문 내용 참고 및 정리



RestController 설정

package com.example.demo.api;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController // REST를 적용한 Controller 사용 표시
@RequestMapping("/api")
public class FirstApiController {

    @GetMapping("hi")
    public String hello() {
        return "hello world";
    }
}

rest_controller 1.png


REST API로 GET 구현

  1. REST API와 @RestController의 특징을 이용하여 게시글의 데이터를 가져올 Controller를 생성한다. com.example.package_name.api 패키지에 ArticleApiController를 생성한다.
  2. @RestController Annotation을 추가하고, 공통적인 Path 지정을 위해 @RequestMapping Annotation을 사용하여 /api로 시작하는 요청임을 지정한다.
  3. ArticleRepository를 자동 주입한 후, @GetMapping들을 추가하여 게시글의 데이터를 가져올 수 있도록 설정한다.
package com.example.demo.api;  
  
import com.example.demo.entity.Article;  
import com.example.demo.repository.ArticleRepository;  
import lombok.extern.slf4j.Slf4j;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.PathVariable;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.RestController;  
  
import java.util.ArrayList;  
  
@RestController // REST를 적용한 Controller 사용 표시  
@RequestMapping("/api")  
@Slf4j // simple logging facade for java  
public class ArticleApiController {  
  
    @Autowired  
    private ArticleRepository articleRepository;  
  
    // GET  
    @GetMapping("articles") // 게시글 전체 조회  
    public ArrayList<Article> index() {  
        return (ArrayList<Article>) articleRepository.findAll();  
    }  
  
    @GetMapping("articles/{id}") // 특정 게시글 조회  
    public Article show(@PathVariable Long id) {  
        return articleRepository.findById(id).orElse(null);  
    }  
}

rest_controller 2.png
rest_controller 3.png


REST API로 POST 구현

  1. ArticleApiController@PostMapping을 추가한다.
    • 이 때 요청의 본문(body)에 있는 데이터를 사용하기 위해선 @RequestBody Annotation을 사용한다.
    • REST API에서 데이터를 생성할 때는 JSON 데이터를 받아와야 하기 때문에 DTO만 작성해서는 데이터를 가져올 수 없다.
package com.example.demo.api;  
  
import com.example.demo.DTO.ArticleForm;  
import com.example.demo.entity.Article;  
import com.example.demo.repository.ArticleRepository;  
import lombok.extern.slf4j.Slf4j;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.web.bind.annotation.*;  
  
import java.util.ArrayList;  
  
@RestController // REST를 적용한 Controller 사용 표시  
@RequestMapping("/api")  
@Slf4j // simple logging facade for java  
public class ArticleApiController {  
  
    @Autowired  
    private ArticleRepository articleRepository;  
  
    // GET  
    @GetMapping("articles") // 게시글 전체 조회  
    public ArrayList<Article> index() {  
        return (ArrayList<Article>) articleRepository.findAll();  
    }  
  
    @GetMapping("articles/{id}") // 특정 게시글 조회  
    public Article show(@PathVariable Long id) {  
        return articleRepository.findById(id).orElse(null);  
    }  
  
    // POST  
    @PostMapping("articles") // 새 글 작성  
    public Article create(@RequestBody ArticleForm dto) { // 요청 시 본문에 포함되는 데이터를 사용  
        Article article = dto.toEntity();  
        return articleRepository.save(article);  
    }  
}

rest_controller 4.png

rest_controller 5.png

rest_controller 6.png


REST API로 PATCH 구현

  1. ArticleApiController@PatchMapping을 추가한다.
    • 이 때 요청의 본문(body)에 있는 데이터를 사용하기 위해선 @RequestBody Annotation을 사용한다.
    • 요청에 포함된 path variable을 사용하기 위해 @PathVariable Annotation도 사용한다.
    • 만약 사용자가 요청하는 id를 가진 데이터가 없거나, 수정할 데이터의 id와 요청 id가 다른 경우엔 수정을 진행하면 안되므로 잘못된 요청 처리를 해줘야 한다.
      • ResponseEntity는 REST API의 응답을 위해 사용하는 클래스로, HTTP 상태 코드, Header, 본문(body)를 보낼 수 있다.
      • HttpStatus는 HTTP 상태 코드를 관리하는 클래스다.
      • HTTP 상태 코드 참고.
분류 코드 field 설명
2xx 성공을 알리는 상태 코드
200 HttpStatus.OK 성공함,
201 HttpStatus.CREATED 작성됨
3xx 리다이렉션(다른 페이지로 이동)을 알리는 상태 코드
301 HttpStatus.MOVED_PERMANENTLY 영구 이동
302 HttpStatus.MOVED_TEMPORARILY 임시 이동
304 HttpStatus.NOT_MODIFIED 수정되지 않음. 요청의 응답으로 캐시를 사용
4xx 요청 요류를 알리는 상태 코드. 요청 자체에 오류가 있을 때 표시
400 HttpStatus.BAD_REQUEST 잘못된 요청
401 HttpStatus.UNAUTHORIZED 권한 없음
403 HttpStatus.FORBIDDEN 금지됨. 401과 비슷함
404 HttpStatus.NOT_FOUND 찾을 수 없음
5xx 서버 오류를 나타내는 상태 코드. 요청은 제대로 왔으나 서버에 오류가 생겼을 때 표시
500 HttpStatus.INTERNAL_SERVER_ERROR 내부 서버 오류
502 HttpStatus.BAD_GATEWAY 불량 게이트웨이
503 HttpStatus.SERVICE_UNAVAILABLE 서비스 이용 불가
package com.example.demo.api;  
  
import com.example.demo.DTO.ArticleForm;  
import com.example.demo.entity.Article;  
import com.example.demo.repository.ArticleRepository;  
import lombok.extern.slf4j.Slf4j;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.http.HttpStatus;  
import org.springframework.http.ResponseEntity;  
import org.springframework.web.bind.annotation.*;  
  
import java.util.ArrayList;  
  
@RestController // REST를 적용한 Controller 사용 표시  
@RequestMapping("/api")  
@Slf4j // simple logging facade for java  
public class ArticleApiController {  
  
    @Autowired  
    private ArticleRepository articleRepository;  
  
    // GET  
    @GetMapping("articles") // 게시글 전체 조회  
    public ArrayList<Article> index() {  
        return (ArrayList<Article>) articleRepository.findAll();  
    }  
  
    @GetMapping("articles/{id}") // 특정 게시글 조회  
    public Article show(@PathVariable Long id) {  
        return articleRepository.findById(id).orElse(null);  
    }  
  
    // POST  
    @PostMapping("articles") // 새 글 작성  
    public Article create(@RequestBody ArticleForm dto) { // 요청 시 본문에 포함되는 데이터를 사용  
        Article article = dto.toEntity();  
        return articleRepository.save(article);  
    }  
  
    // PATCH  
    @PatchMapping("articles/{id}") // 기존 글 수정  
    public ResponseEntity<Article> update( // 반환형에 유의한다.
	    @PathVariable Long id,
	    @RequestBody ArticleForm dto
     ) { // 요청 시 본문에 포함되는 데이터를 사용  
        // 들어온 데이터 변환  
        Article article = dto.toEntity();  
          
        // 수정 대상 조회  
        Article target = articleRepository.findById(id).orElse(null);  
  
        // 잘못된 요청 처리  
        // 해당 id의 데이터가 없거나, 요청 id와 수정할 데이터의 id가 다른 경우  
        if (target == null || id != article.getId()) {  
            // 400 코드 전송  
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);  
        }  
  
        // DB에 수정 내용 저장  
        target.patch(article); // 기존 데이터에 새 데이터 붙이기(일부만 수정 시 null 방지)  
        Article updated = articleRepository.save(article);  
        // 200 코드와 수정 결과 전송  
        return ResponseEntity.status(HttpStatus.OK).body(updated);  
    }  
}
  1. 만약 데이터에서 일부 항목만 수정하는 경우, 입력 값에는 일부 데이터가 없는 상태로 DB에 넘어가게 되어 기존 데이터가 삭제될 수 있다. 따라서 Article에 새 데이터를 넘겨 받는 경우 해당 데이터에 값이 존재하는지 여부에 따라 기존 데이터를 남겨두거나 대체하는 메소드를 추가해야 한다.
package com.example.demo.entity;  
  
import jakarta.persistence.*;  
import lombok.AllArgsConstructor;  
import lombok.Data;  
import lombok.NoArgsConstructor;  
  
@Entity // Entity임을 명시하는 Annotation@Data // Lombok  
@NoArgsConstructor  
public class Article {  
  
    @Id // 식별자  
    @GeneratedValue(strategy = GenerationType.IDENTITY)  
    private Long id;  
  
    @Column // DB에서 열과 대응되는 속성  
    private String title;  
  
    @Column  
    private String content;  
  
    public Article(Long id, String title, String content) {  
        this.id = id;  
        this.title = title;  
        this.content = content;  
    }  
  
    public void patch(Article article) { // 새 데이터가 들어올 때 값이 있을 때만 수정  
        if (article.title != null) {  
            this.title = article.title;  
        }  
        if (article.content != null) {  
            this.content = article.content;  
        }  
    }  
}

rest_controller 7.png

rest_controller 8.png

rest_controller 9.png

rest_controller 10.png

rest_controller 11.png


REST API로 DELETE 구현

package com.example.demo.api;  
  
import com.example.demo.DTO.ArticleForm;  
import com.example.demo.entity.Article;  
import com.example.demo.repository.ArticleRepository;  
import lombok.extern.slf4j.Slf4j;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.http.HttpStatus;  
import org.springframework.http.ResponseEntity;  
import org.springframework.web.bind.annotation.*;  
  
import java.util.ArrayList;  
  
@RestController // REST를 적용한 Controller 사용 표시  
@RequestMapping("/api")  
@Slf4j // simple logging facade for java  
public class ArticleApiController {  
  
    @Autowired  
    private ArticleRepository articleRepository;  
  
    // GET  
    @GetMapping("articles") // 게시글 전체 조회  
    public ArrayList<Article> index() {  
        return (ArrayList<Article>) articleRepository.findAll();  
    }  
  
    @GetMapping("articles/{id}") // 특정 게시글 조회  
    public Article show(@PathVariable Long id) {  
        return articleRepository.findById(id).orElse(null);  
    }  
  
    // POST  
    @PostMapping("articles") // 새 글 작성  
    public Article create(@RequestBody ArticleForm dto) { // 요청 시 본문에 포함되는 데이터를 사용  
        Article article = dto.toEntity();  
        return articleRepository.save(article);  
    }  
  
    // PATCH  
    @PatchMapping("articles/{id}") // 기존 글 수정  
    public ResponseEntity<Article> update( // 반환형에 유의한다.  
            @PathVariable Long id,  
            @RequestBody ArticleForm dto  
    ) { // 요청 시 본문에 포함되는 데이터를 사용  
        // 들어온 데이터 변환  
        Article article = dto.toEntity();  
  
        // 수정 대상 조회  
        Article target = articleRepository.findById(id).orElse(null);  
  
        // 잘못된 요청 처리  
        // 해당 id의 데이터가 없거나, 요청 id와 수정할 데이터의 id가 다른 경우  
        if (target == null || id != article.getId()) {  
            // 400 코드 전송  
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);  
        }  
  
        // DB에 수정 내용 저장  
        target.patch(article); // 기존 데이터에 새 데이터 붙이기(일부만 수정 시 null 방지)  
        Article updated = articleRepository.save(article);  
        // 200 코드와 수정 결과 전송  
        return ResponseEntity.status(HttpStatus.OK).body(updated);  
    }  
  
    // DELETE  
    @DeleteMapping("articles/{id}")  
    public ResponseEntity<Article> delete(@PathVariable Long id) {  
  
        // 대상 조회  
        Article target = articleRepository.findById(id).orElse(null);  
  
        // 대상이 없으면 잘못된 요청으로 처리 - 400        if (target == null) {  
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);  
        }  
  
        // 대상이 있으면 삭제 진행  
        articleRepository.delete(target);  
        // build() : HTTP 응답의 body가 없는 ResponseEntity 생성  
        return ResponseEntity.status(HttpStatus.OK).build(); 
    }  
}

rest_controller 12.png

rest_controller 13.png

rest_controller 14.png